package com.horizon.cloud.standard.lock;

import com.horizon.cloud.common.util.RandomUtil;
import com.horizon.cloud.common.util.ThreadUtil;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

public class JedisMultiSegmentLock implements Lock {
    public static final int NO_SEG =-1;
    //拿到锁的线程
    private Thread thread;

    //拿到锁的状态
    private volatile boolean isLocked = false;

    //段数
    private final int segAmount;

    public static final int DEFAULT_TIMEOUT = 2000;
    public static final Long WAIT_GAT = Long.valueOf(100);

    //内部的锁
    InnerLock[] innerLocks = null;

    //被锁住的分段
    int segmentIndexLocked = NO_SEG;
    //默认为2000ms
    long expire = 2000L;
    int segmentIndex = 0;

    public JedisMultiSegmentLock(String lockKey, String requestId,
                                int segAmount){
        this.segAmount = segAmount;
        innerLocks = new InnerLock[segAmount];
        for (int i =0;i<this.segAmount;i++) {
            //每一个分段， 加上一个编号
            String innerLockKey = lockKey + ":"+i;
            innerLocks[i] = new InnerLock(innerLockKey, requestId);
        }
        segmentIndex = RandomUtil.randInModLower(this.segAmount);
    }

    @Override
    public void lock() {

    }

    @Override
    public void lockInterruptibly() throws InterruptedException {

    }

    @Override
    public boolean tryLock() {
        return false;
    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        //本地可重入
        if (isLocked && thread == Thread.currentThread()) {
            return true;
        }
        expire = unit != null ?unit.toMillis(time):DEFAULT_TIMEOUT;
        long startMillis = System.currentTimeMillis();
        Long millisToWait = expire;

        boolean localLocked = false;
        int turn = 1;
        InnerLock innerLock = innerLocks[segmentIndex];

        while(!localLocked) {
            localLocked = innerLock.lock(expire);
            if (!localLocked) {
                millisToWait = millisToWait -(System.currentTimeMillis()-
                        startMillis);
                startMillis = System.currentTimeMillis();
                if (millisToWait>0) {
                    //没有超时，继续枪锁
                    ThreadUtil.sleepMilliSeconds(WAIT_GAT);
                    segmentIndex ++;
                    if (segmentIndex>=this.segAmount) {
                        segmentIndex=0;
                    }
                    innerLock = innerLocks[segmentIndex];
                } else {
                    return false;
                }
            } else {
                segmentIndexLocked = segmentIndex;
                isLocked = true;
                localLocked = true;
                thread = Thread.currentThread();
            }
        }
        return isLocked;
    }

    @Override
    public void unlock() {
        if(segmentIndexLocked == NO_SEG) {
            return;
        }
        this.innerLocks[segmentIndexLocked].unlock();
        segmentIndexLocked = NO_SEG;
        thread = null;
        isLocked = false;
    }

    @Override
    public Condition newCondition() {
        return null;
    }
}
